#ifndef WINDOWS_IO_IMPL
#define WINDOWS_IO_IMPL

#include <windows.h>
#include <string>

namespace adapters { namespace detail {

	typedef HANDLE NativeHandleType;
	typedef LARGE_INTEGER NativeOffsetType;

	template <class CharType>
	struct NativeApiTraits;
	
	template <>
	struct NativeApiTraits<char> {

		static inline bool create(NativeHandleType handle, char const * file_name) {
			handle = ::CreateFileA(file_name, GENERIC_WRITE, FILE_SHARE_WRITE, 
							NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
					  );
			if (handle == INVALID_HANDLE_VALUE)
				return false;
			return true;
		}

		static inline bool open(NativeHandleType handle, char const * file_name) {
			handle = ::CreateFileA(file_name, GENERIC_READ, FILE_SHARE_READ, 
						NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
					  );
			if (handle == INVALID_HANDLE_VALUE)
				return false;
			return true;
		}

	};

	template <>
	struct NativeApiTraits<wchar_t> {

		static inline bool create(NativeHandleType handle, wchar_t const * file_name) {
				handle = ::CreateFileW(file_name, GENERIC_WRITE, FILE_SHARE_WRITE, 
							NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
					  );
			if (handle == INVALID_HANDLE_VALUE)
				return false;
			return true;
		}

		static inline bool open(NativeHandleType handle, wchar_t const * file_name) {
			handle = ::CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, 
						NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
					  );
			if (handle == INVALID_HANDLE_VALUE)
				return false;
			return true;
		}

	};

	class DeviceImpl {
	public :
		DeviceImpl() {}
		
		template <class Offset>
		static NativeOffsetType convert2NativeOffset(Offset off) {
			NativeOffsetType native;
			native.QuadPart = off;
			return native;
		}

		template <class OffsetType>
		inline static OffsetType nativeSeek(NativeHandleType handle, NativeOffsetType where) {
			OffsetType pos = -1; NativeOffsetType npos = where;
			if (::SetFilePointerEx(handle, npos, &where, FILE_BEGIN) != FALSE)
				pos = where.QuadPart;
			return pos;
		}
		
		template <class OffsetType>
		inline static OffsetType nativeSeekBeg(NativeHandleType handle) {
			NativeOffsetType off = {0}; 
			::SetFilePointerEx(handle, off, &off, FILE_BEGIN);
			return off.QuadPart;
		}

		template <class OffsetType>
		inline static OffsetType nativeSeekEnd(NativeHandleType handle) {
			NativeOffsetType off = {0}; 
			::SetFilePointerEx(handle, off, &off, FILE_END);
			return off.QuadPart;
		}
		
		template <class OffsetType>
		inline static OffsetType nativeSeekCur(NativeHandleType handle) {
			NativeOffsetType off = {0}; 
			::SetFilePointerEx(handle, off, &off, FILE_CURRENT);
			return off.QuadPart;
		}

		static void nativeClose(NativeHandleType handle) {
			::CloseHandle(handle);
			handle = INVALID_HANDLE_VALUE;
		}

	private :
		DeviceImpl(DeviceImpl const &);
		DeviceImpl& operator=(DeviceImpl const &);
	};

	class WriteDeviceImpl : public DeviceImpl {
	public :
		WriteDeviceImpl() 
			: DeviceImpl(), m_RawHandle(INVALID_HANDLE_VALUE) 
		{}

		~WriteDeviceImpl() {
			::CloseHandle(m_RawHandle);
			m_RawHandle = INVALID_HANDLE_VALUE;
		}

		template <class CharType>
		inline static bool nativeCreateBinary(NativeHandleType handle, std::basic_string<CharType> const & file_name) {
			return NativeApiTraits<CharType>::create(handle, file_name.c_str());
		}

		template <class CharType>
		inline static bool nativeCreateDefault(NativeHandleType handle, std::basic_string<CharType> const & file_name) {
			return NativeApiTraits<CharType>::create(handle, file_name.c_str());
		}

		inline static std::streamsize nativeWrite(NativeHandleType handle, char const * buf, std::streamsize n) {
			DWORD writed = 0;
			if (::WriteFile(handle, buf, n, &writed, NULL) != TRUE) 
				return -1;
			return (int)writed;
		}

		inline NativeHandleType handle() {
			return m_RawHandle;
		}

	private :
		NativeHandleType m_RawHandle;
	};


	class ReadDeviceImpl : public DeviceImpl {
	public :
		ReadDeviceImpl() 
			: DeviceImpl(), m_RawHandle(INVALID_HANDLE_VALUE) 
		{}

		~ReadDeviceImpl() {
			::CloseHandle(m_RawHandle);
			m_RawHandle = INVALID_HANDLE_VALUE;
		}

		template <class CharType>
		inline static bool nativeOpenBinary(NativeHandleType handle, std::basic_string<CharType> const & file_name) {
			return NativeApiTraits<CharType>::open(handle, file_name.c_str());
		}

		template <class CharType>
		inline static bool nativeOpenDefault(NativeHandleType handle, std::basic_string<CharType> const & file_name) {
			return NativeApiTraits<CharType>::open(handle, file_name.c_str());
		}

		inline static std::streamsize nativeRead(NativeHandleType handle, char * buf, std::streamsize n) {
			DWORD readed = 0;
			if (::ReadFile(handle, buf, n, &readed, NULL) != TRUE)
				return -1;
			return (int)readed;
		}

		inline NativeHandleType handle() {
			return m_RawHandle;
		}

	private :
		NativeHandleType m_RawHandle;
	};
	
} 
}

#endif
